#include "uart_obj.h"

/*
        库名:  串口收发框架库 (uart_obj_fw)
        作者： spongebob (gitee)

        其他注意事项：
        注意在使用stm32HAL库时，使用UART DMA接收数据时，通讯线上无干扰可以正常工作，当有干扰发生会产生错误中断，
        如错误有 HAL_UART_ERROR_PE（Parity error）、HAL_UART_ERROR_NE（Noise error）、HAL_UART_ERROR_FE（Frame error）、
        HAL_UART_ERROR_ORE（Overrun error）、HAL_UART_ERROR_DMA（DMA transfer error）
        HAL_UART_ERROR_RTO（Receiver Timeout error），一定注意错误处理，因为发生错误后dma接收将暂停，需要重新初始化dma接收，如果相关库未清除错误标志位，需要手动清除

        为什么dma需要停止传输：
        启用dma传输很正常，但是对于没有IDLE中断的MCU，如果不人为停止，会一直传输

        DMA停止两种本质的区别就是，一种是硬件支持IDLE中断，走中断

        版本更新记录

        v2.0.2
        修改函数api,增加函数重定义

        v2.0.3
        1. 对于只读型数据加上const进行保护，更加规范
        2. 对每个函数进行添加注释讲解

        v2.0.4
        1. 增加USE_STD_LIB 选项 ，使用自定义的printf , 节省内存
        2. 增加USE_UART_TX_STR_DATA 选项 ，用于节省内存

        v2.0.5
        1. 增加USE_STRING_EX_LIB 选项 ，用于变量转字符串 ，可不使用printf
        可变形参方式，节省内存
        2. 优化其他第三方库加载位置

        v2.0.6
        1. 为 string_ex 库的 浮点型增加保护
        增加两项配置：
        USE_INT_64_WIDTH , 整型使用64位宽度
        USE_FLOAT_FULL_WIDTH，浮点使用完全宽度

        v2.0.7
        1. 增加线程安全函数接口，具体实现用用户完成
        2. 发送数据的接口重新优化，使其适配线程安全

        v2.1.0
        此版本有稍微大的改动
        改动如下
        1. 将接收fifo的深度做成动态传入的，不靠宏定义
        这样做的好处是，当定义多个串口对象时，可以使用不同的fifo深度，
        不同的缓冲区大小，可以按需选择更加合理
        因此初始化需要传统rx_fifo_deep和rx_fifo_manage
        其中rx_fifo_deep为fifo深度，rx_fifo_manage为fifo管理变量
        管理变量应是外部一个全局结构体数组，数组值大小和rx_fifo_deep相同
        传入时传入该结构体地址

        v2.2.0
        1. 增加串口发送接收模式选择，
        默认宏定义不开的情况下，是必须要初始化收发参数的，
        宏定义开启后，选择只发送，那么初始化时只需要初始化发送参数即可，
        选择只接收，那么初始化时只需要初始化接收参数即可，
        收发都选择，那么都需要初始化

        v2.2.1
        细节修复
        1. 修复如果关闭DMA模式，会编译报错的问题
        2. 配置默认不开启第三方库，避免新手移植时，
        只添加uart_obj.c不添加另外两个第三方库（xprintf.c string_ex.c）导致的报错

        v2.2.2
        参数检查全部使用assert
        原因：目前初始化代码占用过大，而且主要是参数检查和日志方面的信息，
        如果使用assert能有效减少这一部分代码的占用
        1. 删除了 USE_CHECK_INPUT_HANDLE_PARA 配置项
        2. 增加了 USE_UART_OBJ_FW_ASSERT 配置项，用于开启断言功能

        进一步降低flash的占用
        下个版本的计划： 结构体相同的内容进行封装，进一步提高可读性

        v2.2.3
        1. 优化主结构体，提高封装性，继续降低flash的占用
        init 专门为一个结构体

        v2.2.4
        1. 优化文档使用utf-8编码，同时使用代码格式化工具格式化代码

        v2.2.5
        1. 优化初始化传入的参数为只读类型参数
*/

void uart_obj_fw_assert(char* file, uint32_t line)
{
    printf("[uart obj fw assert] file: %s , line: %d \r\n", file, line);
    while (1)
    {
    }
}

/*---------------------------------------内部使用-----------------------------------*/
#if (USE_UART_TX_RX_MODE_SELECT == 1)
// 串口模式字符串数组，用于打印
const char sg_uart_mode_str[][16] = {"tx only", "rx only", "tx rx"};
#endif
static uart_obj_sta_t uart_obj_rx_fifo_write(uart_obj_t* obj);
/*--------------------------------------------------------------------------------------*/
#if (USE_ONE_BYTE_IT_MODE == 1)

/**
 * @brief 单字节中断初始化串口
 * @param obj 串口对象
 * @param init_param 串口初始化参数
 * @return uart_obj_sta_t 状态值
 * @note
 * 该函数初始化串口，并设置接收参数，包括接收缓冲区基地址，单个缓冲区大小，接收超时时间，接收回调函数
 */
uart_obj_sta_t uart_obj_it_init(uart_obj_t* obj,const uart_init_t* init_param)
{
/*---------------------------------------------------输入参数检查------------------------------------------------*/
#if (USE_UART_OBJ_FW_ASSERT == 1)
    UART_OBJ_FW_ASSERT(obj == NULL);               // obj 对象为空
    UART_OBJ_FW_ASSERT(init_param == NULL);        // init_param 对象为空
#endif

    /*---------------------------------------------------串口框架当前模式------------------------------------------------*/
#if (USE_UART_TX_RX_MODE_SELECT == 1)
#if (USE_UART_OBJ_FW_ASSERT == 1)
    UART_OBJ_FW_ASSERT(init_param->uart_mode > FW_UART_TX_RX);
#endif
    // obj->init.uart_mode = init_param->uart_mode;
#if (USE_INFO_LOG == 1)
    UART_INFO_LOG("[uart it mode]: uart fw mode: %s\r\n", sg_uart_mode_str[init_param->uart_mode]);
#endif
#endif        // USE_UART_TX_RX_MODE_SELECT

#if (USE_UART_TX_RX_MODE_SELECT == 1)
    // 判断是否有发送模式
    if (obj->init.uart_mode != FW_UART_RX_ONLY)        // 如果不是只接收模式，那么需要初始化发送参数
    {
#endif        // (USE_UART_TX_RX_MODE_SELECT == 1)
        /*---------------------------------------------------设置发送参数-发送缓冲区-----------------------------------------*/
        // 发送缓冲区初始化
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->tx_buf_addr == NULL);            // 发送缓冲区为空
        UART_OBJ_FW_ASSERT(init_param->tx_buf_size == 0);               // 发送缓冲区大小为0
        UART_OBJ_FW_ASSERT(init_param->uart_tx_port_cb == NULL);        // 发送回调函数为空
#endif

        // obj->init.tx_buf_addr     = init_param->tx_buf_addr;
        // obj->init.tx_buf_size     = init_param->tx_buf_size;
        // obj->init.uart_tx_port_cb = init_param->uart_tx_port_cb;
/*------------------------------------------------------设置发送参数-线程安全-----------------------------------------------------------------*/
#if (USE_THREAD_SAFE == 1)
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->tx_lock_cb == NULL);          // 发送锁函数为空
        UART_OBJ_FW_ASSERT(init_param->tx_unlock_cb == NULL);        // 发送解锁函数为空
#endif
        // obj->init.tx_lock_cb   = init_param->tx_lock_cb;
        // obj->init.tx_unlock_cb = init_param->tx_unlock_cb;
#endif        // (USE_THREAD_SAFE == 1)

#if (USE_UART_TX_RX_MODE_SELECT == 1)
    }
#endif        // (USE_UART_TX_RX_MODE_SELECT == 1)

#if (USE_UART_TX_RX_MODE_SELECT == 1)
    // 判断是否有接收模式
    if (obj->init.uart_mode != FW_UART_TX_ONLY)        // 如果不是只发送模式，那么需要初始化接收参数
    {
#endif        // (USE_UART_TX_RX_MODE_SELECT == 1)
/*---------------------------------------------------设置接收参数-接收缓冲区-----------------------------------------*/
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->rx_fifo_deep == 0);                    // 接收fifo深度为0
        UART_OBJ_FW_ASSERT(init_param->rx_fifo_manage == NULL);               // 接收fifo管理变量为空
        UART_OBJ_FW_ASSERT(init_param->rx_fifo_buf_base_addr == NULL);        // 接收fifo缓冲区基地址为空
        UART_OBJ_FW_ASSERT(init_param->rx_fifo_buf_size_single == 0);         // 接收fifo单个缓冲区大小为0
        // 总大小必须等于单个大小乘以fifo深度
        UART_OBJ_FW_ASSERT((init_param->rx_fifo_buf_size_single * init_param->rx_fifo_deep) != init_param->rx_fifo_buf_size_total);
#endif
        /*
                由于缓冲区为动态确定，因此需要用户传入缓冲区深度
                和管理变量的地址
        */
        // obj->init.rx_fifo_deep   = init_param->rx_fifo_deep;          // 设置接收fifo深度
        // obj->init.rx_fifo_manage = init_param->rx_fifo_manage;        // 设置接收fifo管理变量

        // obj->init.rx_fifo_buf_base_addr   = init_param->rx_fifo_buf_base_addr;
        // obj->init.rx_fifo_buf_size_single = init_param->rx_fifo_buf_size_single;
        // obj->init.rx_fifo_buf_size_total  = init_param->rx_fifo_buf_size_total;

        // // 初始化时，当前地址指向第一个缓冲区
        // obj->rx_fifo_buf_cur_addr = obj->init.rx_fifo_buf_base_addr;

/*------------------------------------------------设置接收参数-接收回调函数------------------------------------------------*/
// 串口fifo接收处理函数
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->uart_fifo_rx_cb == NULL);        // 接收回调函数为空
#endif
        // obj->init.uart_fifo_rx_cb = init_param->uart_fifo_rx_cb;
/*---------------------------------------------------设置接收参数-断帧时间------------------------------------------------------*/
// 接收断帧时间
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->rx_time_out_set == 0);        // 接收超时时间为0
#endif

        // obj->init.rx_time_out_set = init_param->rx_time_out_set;

/*---------------------------------------------------设置接收参数-接收中断函数---------------------------------------------------*/
// 串口单字节中断函数接口
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->uart_get_rx_one_byte_cb == NULL);        // 接收回调函数为空
#endif

        // obj->init.uart_get_rx_one_byte_cb = init_param->uart_get_rx_one_byte_cb;

/*---------------------------------------------------设置串口接收启停函数--------------------------------------------------------------*/
// 串口fifo接收启停函数
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->uart_fifo_rx_stop_cb == NULL);         // fifo 接收停止函数为空
        UART_OBJ_FW_ASSERT(init_param->uart_fifo_rx_start_cb == NULL);        // fifo 接收开始为空
#endif

        // obj->init.uart_fifo_rx_stop_cb  = init_param->uart_fifo_rx_stop_cb;
        // obj->init.uart_fifo_rx_start_cb = init_param->uart_fifo_rx_start_cb;
        // obj->init.uart_fifo_rx_start_cb(obj->rx_fifo_buf_cur_addr,
        //                                 obj->init.rx_fifo_buf_size_single);        // 启动接收
#if (USE_UART_TX_RX_MODE_SELECT == 1)
    }
#endif        // (USE_UART_TX_RX_MODE_SELECT == 1)
    /*------------------------------------------------------------------------------------------------------------------------------------*/
    // 初始化完成
    obj->init = *init_param;
    // 初始化时，当前地址指向第一个缓冲区
    obj->rx_fifo_buf_cur_addr = obj->init.rx_fifo_buf_base_addr;
    obj->init.uart_fifo_rx_start_cb(obj->rx_fifo_buf_cur_addr, obj->init.rx_fifo_buf_size_single);        // 启动接收
    obj->init_flag.bits.init_mode_it = 1;
#if (USE_INFO_LOG == 1)
    UART_INFO_LOG("[uart it mode]: init success\r\n");
    UART_INFO_LOG("[uart it mode]: use RAM %d byte\r\n", (uint16_t)sizeof(uart_obj_t));
    UART_INFO_LOG("[uart it mode]: fifo size : %d\r\n", obj->init.rx_fifo_deep);
    UART_INFO_LOG("[uart it mode]: single buf size : %d\r\n", obj->init.rx_fifo_buf_size_single);
    UART_INFO_LOG("[uart it mode]: uart obj fw version %s\r\n\r\n", UART_OBJ_FW_VERSION);
#endif
    return uart_obj_ok;
}

/**
 * @brief 单字节中断断帧函数
 * @param obj 串口对象
 * @return uart_obj_sta_t 状态值
 * @note
 * 该函数需要定时调用，如1ms，当串口总线上空闲超过一段时间后判定为收到一帧数据
 */
uart_obj_sta_t uart_obj_it_rx_frame_break(uart_obj_t* obj)
{

#if (USE_UART_OBJ_FW_ASSERT == 1)
    UART_OBJ_FW_ASSERT(obj == NULL);

#endif

    if (obj->init_flag.bits.init_mode_it == 0)
    {
        return uart_obj_not_init;
    }

#if (USE_UART_TX_RX_MODE_SELECT == 1)
    // 当前模式处于只发送模式 ，不可以调用接收函数
    if (obj->init.uart_mode == FW_UART_TX_ONLY)
    {
        return uart_obj_mode_tx_only;
    }
#endif

    // 无数据接收
    if (obj->rx_cnt_now == 0)
    {
        obj->rx_cnt_bef = 0;
        return uart_obj_ok;
    }

    // 有新数据到达
    if (obj->rx_cnt_now != obj->rx_cnt_bef)
    {
        obj->rx_cnt_bef  = obj->rx_cnt_now;
        obj->rx_time_out = 0;        // 重置超时计数器
        return uart_obj_ok;
    }

    // 超时处理（单次触发核心逻辑）
    if (obj->rx_time_out < obj->init.rx_time_out_set)
    {
        obj->rx_time_out++;
        if (obj->rx_time_out >= obj->init.rx_time_out_set)
        {
            // 写入 fifo 缓冲区
            uart_obj_rx_fifo_write(obj);
            obj->rx_cnt_now = 0;
        }
    }
    return uart_obj_ok;
}

/*
        中断函数
        单字节中断的方式存储收到的数据

        该函数放在串口中断的位置，中断方式来触发该函数

        为什么会有个t_rx_irq_status，因为串口中断有好几种，不止是串口接收会进入中断
        0 无中断事件 1 为有中断事件

        注意: 单字节中断不适合高波特率频繁中断，对系统性能占用太大了
*/

/**
 * @brief 单字节中断接收中断函数（串口接收中断irq调用）
 * @param obj 串口对象
 * @return uart_obj_sta_t 状态值
 * @note 该函数在串口接收中断中调用，接收到数据后，写入接收缓冲区
 */
uart_obj_sta_t uart_obj_it_rx_byte_irq(uart_obj_t* obj)
{
#if (USE_UART_OBJ_FW_ASSERT == 1)
    UART_OBJ_FW_ASSERT(obj == NULL);
#endif

    if (obj->init_flag.bits.init_mode_it == 0)
    {
        return uart_obj_not_init;
    }

#if (USE_UART_TX_RX_MODE_SELECT == 1)
    // 当前模式处于只发送模式 ，不可以调用接收函数
    if (obj->init.uart_mode == FW_UART_TX_ONLY)
    {
        return uart_obj_mode_tx_only;
    }
#endif

    if (obj->rx_buf_lock == 1)        // 接收缓冲区被锁定，不允许写入数据
    {
        return uart_obj_rx_buf_lock;
    }

    // 读取数据以及状态
    if (obj->init.uart_get_rx_one_byte_cb(&(obj->rx_cache_one_byte)) == 0)
    {
        return uart_obj_no_rx_it_event;        // 无串口接收中断事件
    }

    // 有串口接收中断发生，读取数据
    obj->rx_fifo_buf_cur_addr[obj->rx_cnt_now++] = obj->rx_cache_one_byte;
    if (obj->rx_cnt_now >= (obj->init.rx_fifo_buf_size_single - 1))        //-1是为了留一个空字符
    {
        // 缓冲区写满了，立即处理
        uart_obj_rx_fifo_write(obj);
        obj->rx_cnt_now = 0;
    }

    return uart_obj_ok;
}

#endif

/**
 * @brief 串口发送
 * tips: 内部调用，用户无需关心
 */
static void uart_obj_tx_check_call_user_cb(uart_obj_t* obj, const void* udata, uint16_t len)
{
    // 不必检查参数，外部已经检查过了
    obj->tx_len = (len > obj->init.tx_buf_size) ? obj->init.tx_buf_size : len;
    memcpy(obj->init.tx_buf_addr, udata, obj->tx_len);
    obj->init.uart_tx_port_cb(obj->init.tx_buf_addr, obj->tx_len);
}

#if (USE_UART_TX_BIN_DATA == 1)
// printf开启该函数必须也要开启
/*
        发送二进制的数据
*/

/**
 * @brief 发送二进制数据
 * @param obj 串口对象
 * @param udata 待发送数据，任意数据类型
 * @param len 待发送数据长度
 * @return uart_obj_sta_t 状态值
 */
uart_obj_sta_t uart_obj_tx_bin_data(uart_obj_t* obj, const void* udata, uint16_t len)
{

#if (USE_UART_OBJ_FW_ASSERT == 1)
    UART_OBJ_FW_ASSERT(obj == NULL);
    UART_OBJ_FW_ASSERT(udata == NULL);
    UART_OBJ_FW_ASSERT(len == 0);
#endif

    // 检查串口是否初始化
    if (obj->init_flag.value == 0)
    {
        return uart_obj_not_init;
    }

#if (USE_UART_TX_RX_MODE_SELECT == 1)
    if (obj->init.uart_mode == FW_UART_RX_ONLY)
    {
        return uart_obj_mode_rx_only;
    }
#endif

    // 线程安全 加锁
#if (USE_THREAD_SAFE == 1)
    obj->init.tx_lock_cb();
#endif
    // 调用用户的接口发送数据
    uart_obj_tx_check_call_user_cb(obj, udata, len);
    // 线程安全 解锁
#if (USE_THREAD_SAFE == 1)
    obj->init.tx_unlock_cb();
#endif
    return uart_obj_ok;
}
#endif

#if (USE_UART_TX_STR_DATA == 1)

/**
 * @brief 发送字符串数据
 * @param obj 串口对象
 * @param str 待发送字符串
 * @return uart_obj_sta_t 状态值
 */
uart_obj_sta_t uart_obj_tx_str_data(uart_obj_t* obj, const char* str)
{
#if (USE_UART_OBJ_FW_ASSERT == 1)
    UART_OBJ_FW_ASSERT(obj == NULL);
    UART_OBJ_FW_ASSERT(str == NULL);

#endif

    // 检查串口是否初始化
    if (obj->init_flag.value == 0)
    {
        return uart_obj_not_init;
    }

#if (USE_UART_TX_RX_MODE_SELECT == 1)
    if (obj->init.uart_mode == FW_UART_RX_ONLY)
    {
        return uart_obj_mode_rx_only;
    }
#endif

    // 线程安全 加锁
#if (USE_THREAD_SAFE == 1)
    obj->init.tx_lock_cb();
#endif
    // 调用用户的接口发送数据
    uart_obj_tx_check_call_user_cb(obj, str, strlen(str));
    // 线程安全 解锁
#if (USE_THREAD_SAFE == 1)
    obj->init.tx_unlock_cb();
#endif
    return uart_obj_ok;
}

#endif

#if (USE_UART_PRINTF == 1)
/**
 * @brief 串口printf函数
 * @param obj 串口对象
 * @param fmt printf格式化字符串
 * @param ... 格式化参数
 * @return uart_obj_sta_t 状态值
 */
uart_obj_sta_t uart_obj_printf(uart_obj_t* obj, const char* fmt, ...)
{
#if (USE_UART_PRINTF_BUF_STATIC == 1)
    // 静态缓冲区
    static char printf_buf[UART_PRINTF_BUF_SIZE] = {0};
#else
    // 动态缓冲区
    char printf_buf[UART_PRINTF_BUF_SIZE] = {0};
#endif
    va_list ap;

#if (USE_UART_OBJ_FW_ASSERT == 1)
    UART_OBJ_FW_ASSERT(obj == NULL);
    UART_OBJ_FW_ASSERT(fmt == NULL);
#endif

    // 检查串口是否初始化
    if (obj->init_flag.value == 0)
    {
        return uart_obj_not_init;
    }

#if (USE_UART_TX_RX_MODE_SELECT == 1)
    if (obj->init.uart_mode == FW_UART_RX_ONLY)
    {
        return uart_obj_mode_rx_only;
    }
#endif

    // 线程安全 加锁
#if (USE_THREAD_SAFE == 1)
    obj->init.tx_lock_cb();
#endif

    va_start(ap, fmt);
#if (USE_STD_LIB == 1)
    // 标准库printf
    vsnprintf(printf_buf, sizeof(printf_buf) - 1, fmt, ap);
#else
    // xprintf库
    my_vsnprintf(printf_buf, sizeof(printf_buf) - 1, fmt, ap);
#endif
    va_end(ap);
    // 调用用户的接口发送数据
    uart_obj_tx_check_call_user_cb(obj, printf_buf, strlen(printf_buf));
    // 线程安全 解锁
#if (USE_THREAD_SAFE == 1)
    obj->init.tx_unlock_cb();
#endif
    return uart_obj_ok;
}
#endif

/*-------------------------------------------------------------dma
 * 模式------------------------------------------------------- */
#if (USE_DMA_MODE == 1)

/**
 * @brief 串口dma模式初始化
 * @param obj 串口对象
 * @param init_param 串口初始化参数
 * @return uart_obj_sta_t 状态值
 * @note
 * 该函数初始化串口，并设置接收参数，包括接收缓冲区基地址，单个缓冲区大小，接收超时时间，接收回调函数
 */
uart_obj_sta_t uart_obj_dma_init(uart_obj_t* obj,const uart_init_t* init_param)
{
#if (USE_UART_OBJ_FW_ASSERT == 1)
    UART_OBJ_FW_ASSERT(obj == NULL);
    UART_OBJ_FW_ASSERT(init_param == NULL);
#endif

/*---------------------------------------------------串口框架当前模式------------------------------------------------*/
#if (USE_UART_TX_RX_MODE_SELECT == 1)
#if (USE_UART_OBJ_FW_ASSERT == 1)
    UART_OBJ_FW_ASSERT(init_param->uart_mode > FW_UART_TX_RX);
#endif
    // obj->init.uart_mode = init_param->uart_mode;
#if (USE_INFO_LOG == 1)
    UART_INFO_LOG("[uart dma mode]: uart fw mode: %s\r\n", sg_uart_mode_str[init_param->uart_mode]);
#endif
#endif        // USE_UART_TX_RX_MODE_SELECT

/*-----------------------------------------------------------发送参数设置---------------------------------------------------------------*/
#if (USE_UART_TX_RX_MODE_SELECT == 1)
    // 判断是否有发送模式
    if (obj->init.uart_mode != FW_UART_RX_ONLY)        // 如果不是只接收模式，那么需要初始化发送参数
    {
#endif        // (USE_UART_TX_RX_MODE_SELECT == 1)
/*---------------------------------------------------设置发送参数-发送缓冲区-----------------------------------------*/
// 发送缓冲区初始化
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->tx_buf_addr == NULL);
        UART_OBJ_FW_ASSERT(init_param->uart_tx_port_cb == NULL);
        UART_OBJ_FW_ASSERT(init_param->tx_buf_size == 0);
#endif

        // obj->init.tx_buf_addr     = init_param->tx_buf_addr;
        // obj->init.tx_buf_size     = init_param->tx_buf_size;
        // obj->init.uart_tx_port_cb = init_param->uart_tx_port_cb;
/*------------------------------------------------------设置发送参数-线程安全-----------------------------------------------------------------*/
#if (USE_THREAD_SAFE == 1)
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->tx_lock_cb == NULL);
        UART_OBJ_FW_ASSERT(init_param->tx_unlock_cb == NULL);
#endif
        // obj->init.tx_lock_cb   = init_param->tx_lock_cb;
        // obj->init.tx_unlock_cb = init_param->tx_unlock_cb;
#endif        // (USE_THREAD_SAFE == 1)

#if (USE_UART_TX_RX_MODE_SELECT == 1)
    }
#endif        // (USE_UART_TX_RX_MODE_SELECT == 1)

/*-----------------------------------------------------------接收参数设置---------------------------------------------------------------*/
#if (USE_UART_TX_RX_MODE_SELECT == 1)
    // 判断是否有接收模式
    if (obj->init.uart_mode != FW_UART_TX_ONLY)        // 如果不是只发送模式，那么需要初始化接收参数
    {
#endif        // (USE_UART_TX_RX_MODE_SELECT == 1)
        /*--------------------------------------------设置接收参数-缓冲区----------------------------------------------*/

#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->rx_fifo_deep == 0);
        UART_OBJ_FW_ASSERT(init_param->rx_fifo_manage == NULL);
        UART_OBJ_FW_ASSERT(init_param->rx_fifo_buf_base_addr == NULL);
        UART_OBJ_FW_ASSERT(init_param->rx_fifo_buf_size_single == 0);
        UART_OBJ_FW_ASSERT((init_param->rx_fifo_buf_size_single * init_param->rx_fifo_deep) != init_param->rx_fifo_buf_size_total);
#endif

        // obj->init.rx_fifo_deep   = init_param->rx_fifo_deep;          // 设置接收fifo深度
        // obj->init.rx_fifo_manage = init_param->rx_fifo_manage;        // 设置接收fifo管理变量

        // obj->init.rx_fifo_buf_base_addr   = init_param->rx_fifo_buf_base_addr;          // 缓冲区基地址
        // obj->init.rx_fifo_buf_size_single = init_param->rx_fifo_buf_size_single;        // 单个缓冲区大小
        // obj->init.rx_fifo_buf_size_total  = init_param->rx_fifo_buf_size_total;         // 接收缓冲区总大小 = 单个缓冲区大小
        //                                                                            // * FIFO深度
        // // 初始化时，当前地址指向第一个缓冲区
        // obj->rx_fifo_buf_cur_addr = obj->init.rx_fifo_buf_base_addr;        // 当前缓冲区地址

/*------------------------------------------------设置接收参数-接收fifo回调函数------------------------------------------------*/
// 串口fifo接收处理函数
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->uart_fifo_rx_cb == NULL);
#endif
        // obj->init.uart_fifo_rx_cb = init_param->uart_fifo_rx_cb;

        /*-------------------设置接收参数-dma
         * 无idle设置-获取dma当前剩余个数---------------------------*/
#if (USE_DMA_NOT_HAVE_RX_IDLE_MODE == 1)
// 获取当前dma接收的字节数
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->get_uart_dma_rx_current_cnt_cb == NULL);
#endif

        // obj->init.get_uart_dma_rx_current_cnt_cb = init_param->get_uart_dma_rx_current_cnt_cb;
/*--------------------------设置接收参数-dma
 * 无idle设置-断帧时间---------------------------*/
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->rx_time_out_set == 0);
#endif
        // obj->init.rx_time_out_set = init_param->rx_time_out_set;
#endif        // (USE_DMA_NOT_HAVE_RX_IDLE_MODE == 1)

/*------------------------------设置接收参数- dma
 * 启停函数--------------------------------------*/
#if (USE_UART_OBJ_FW_ASSERT == 1)
        UART_OBJ_FW_ASSERT(init_param->uart_fifo_rx_stop_cb == NULL);
        UART_OBJ_FW_ASSERT(init_param->uart_fifo_rx_start_cb == NULL);
#endif

        // obj->init.uart_fifo_rx_stop_cb  = init_param->uart_fifo_rx_stop_cb;
        // obj->init.uart_fifo_rx_start_cb = init_param->uart_fifo_rx_start_cb;
        // obj->init.uart_fifo_rx_start_cb(obj->rx_fifo_buf_cur_addr, obj->init.rx_fifo_buf_size_single);
#if (USE_UART_TX_RX_MODE_SELECT == 1)
    }
#endif        // (USE_UART_TX_RX_MODE_SELECT == 1)
    // 初始化完成
    // obj->init = *init_param;

    memcpy(&obj->init, init_param, sizeof(uart_init_t));

    // 初始化时，当前地址指向第一个缓冲区
    obj->rx_fifo_buf_cur_addr = obj->init.rx_fifo_buf_base_addr;
    obj->init.uart_fifo_rx_start_cb(obj->rx_fifo_buf_cur_addr, obj->init.rx_fifo_buf_size_single);        // 启动接收
    obj->init_flag.bits.init_mode_dma = 1;
#if (USE_INFO_LOG == 1)
    UART_INFO_LOG("[uart dma mode]: init success\r\n");
    UART_INFO_LOG("[uart dma mode]: uart obj use RAM %d Byte\r\n", sizeof(uart_obj_t));
    UART_INFO_LOG("[uart dma mode]: fifo size : %d\r\n", obj->init.rx_fifo_deep);
    UART_INFO_LOG("[uart dma mode]: single buf size : %d\r\n", obj->init.rx_fifo_buf_size_single);
    UART_INFO_LOG("[uart dma mode]: uart obj fw version %s\r\n\r\n", UART_OBJ_FW_VERSION);
#endif
    return uart_obj_ok;
}

/**
 * @brief dma模式接收空闲中断回调函数(有idle中断调用)
 * @param obj 串口对象
 * @param len 接收到的数据长度
 * @return uart_obj_sta_t 状态值
 * @note 当idle中断发送，调用该函数，该函数默认dma传输已经停止，
 * 在函数内部不再调用停止dma传输的api，该函数只负责写入fifo
 */
uart_obj_sta_t uart_obj_dma_rx_idle_finish(uart_obj_t* obj, uint16_t len)
{
#if (USE_UART_OBJ_FW_ASSERT == 1)
    UART_OBJ_FW_ASSERT(obj == NULL);
    UART_OBJ_FW_ASSERT(len == 0);
#endif

    if (obj->init_flag.bits.init_mode_dma == 0)
    {
        return uart_obj_not_init;
    }

#if (USE_UART_TX_RX_MODE_SELECT == 1)
    if (obj->init.uart_mode == FW_UART_TX_ONLY)
    {
        return uart_obj_mode_tx_only;
    }
#endif

    obj->rx_cnt_now = len;
    // 写入 fifo 缓冲区
    uart_obj_rx_fifo_write(obj);

    return uart_obj_ok;
}

/*
        dma 断帧函数，用于没有或者不使用IDLE中断的情况，定时调用,推荐1ms
*/

#if (USE_DMA_NOT_HAVE_RX_IDLE_MODE == 1)

/**
 * @brief dma模式 超时断帧函数(无idle中断调用)
 * @param obj 串口对象
 * @return uart_obj_sta_t 状态值
 * @note 定时调用该函数，进行数据断帧处理，无idle，或者想用定时器来触发断帧处理
 */
uart_obj_sta_t uart_obj_dma_rx_frame_break(uart_obj_t* obj)
{
#if (USE_UART_OBJ_FW_ASSERT == 1)
    UART_OBJ_FW_ASSERT(obj == NULL);
#endif

    if (obj->init_flag.bits.init_mode_dma == 0)
    {
        return uart_obj_not_init;
    }

#if (USE_UART_TX_RX_MODE_SELECT == 1)
    if (obj->init.uart_mode == FW_UART_TX_ONLY)
    {
        return uart_obj_mode_tx_only;
    }
#endif

    /*
            查询dma计数寄存器，一段时间内是否有变化，连续一段时间没有变化，则认为接收到一帧数据
    */

#if USE_DMA_RX_CNT_MODE == USE_DMA_RX_POSITIVE_CNT        // 正计模式 获取到的计数值为 0 - rx_buf_size
    obj->rx_cnt_now = obj->get_uart_dma_rx_current_cnt_cb();
#elif USE_DMA_RX_CNT_MODE == USE_DMA_RX_NEGATIVE_CNT        // 反计模式 获取到的计数值为 rx_buf_size - 0
    obj->rx_cnt_now = obj->init.rx_fifo_buf_size_single - obj->init.get_uart_dma_rx_current_cnt_cb();
#endif

    // 无数据接收
    if (obj->rx_cnt_now == 0)
    {
        obj->rx_cnt_bef = 0;
        return uart_obj_ok;
    }

    // 有新数据到达
    if (obj->rx_cnt_now != obj->rx_cnt_bef)
    {
        obj->rx_cnt_bef  = obj->rx_cnt_now;
        obj->rx_time_out = 0;        // 重置超时计数器
        return uart_obj_ok;
    }

    // 超时处理（单次触发核心逻辑）
    if (obj->rx_time_out < obj->init.rx_time_out_set)
    {
        obj->rx_time_out++;
        if (obj->rx_time_out >= obj->init.rx_time_out_set)
        {
            // 无idle需要手动停止dma传输 该停止只是本次传输结束的停止
            obj->init.uart_fifo_rx_stop_cb();
            // 写入 fifo 缓冲区
            uart_obj_rx_fifo_write(obj);
            obj->rx_cnt_now = 0;
        }
    }
    return uart_obj_ok;
}
#endif        // USE_DMA_NOT_HAVE_RX_IDLE_MODE

#endif        // USE_DMA_MODE

/*-----------------------------------------------FIFO
 * 队列实现------------------------------------------------------------*/
/*
        普通单缓冲区的缺陷：
        在单缓冲区的情况下，写入和读出数据都是操作同一个缓冲区，
        很容易出现数据覆盖的情况，当你正在读出数据解码时，此时的数据应该是锁定的，
        不可以被新数据覆盖，但如果没有考虑到这一点就会出现解码出错，数据错乱的情况

        数据段接收本身的时间+串口超时判定帧结束的时间+通讯框架读取数据的延时+解码的时间

        freeRTOS 队列的思考
        freeROS的队列写入和读出都是基于创建时每个元素的大小，如果使用拷贝原数据的方式，
        假如元素大小设置的1K字节，读写都是基于1K字节的。
        如果使用基于指针方式，元素大小就是指针的大小，读写也都是基于指针的大小，
        该指针当然是指向你的缓冲区的，freeRTOS
   队列的作用就变成了管理你的缓冲区。

        显然第二种指针方式是高效的

        需要注意一个细节，由于FIFO的是指针，不是原数据，用指针访问数据，读取以后，
        并不意味着真实数据已经被消费，如果此时你正在解码，忽略了这个问题，会出现以下情况：
        假设FIFO深度只有一层，生产了1包数据，你开始消费，由于读取出指针立刻认为数据消费完成，
        写入下一包数据，但此时你正在解码上一包数据，解码出错，导致数据错乱。
        FIFO深度加大会解决这个问题吗，不会！在到达FIFO深度临界以后一样和一层一样的，
        因为读取出指针立刻认为数据消费完成
        总结：对于操作指针的方式，数据消费完成并不是读取出来就认为完成，需要你处理以后，才能认为完成，
        当然如果FIFO用拷贝原数据的方式不会有这个问题，因为你拷贝另外备份了这个数据，就算你使用读取函数将消费+1也没事，
        你解码的数据时基于你备份的数据，不会出错。

        开发计划
        串口还是基于串口超时断帧方式来确定一包数据，当一个缓冲区用完也会认为收到了一包数据，也就是一包消耗一个缓冲区
        如果遇到一次只发一个字节，也会消耗一个缓冲区
        如果遇到一次发大量的数据，那么在初始化阶段时，单个缓冲区的大小就不可以小于计划传输数据的最大大小

        准备使用一个大缓冲区，包含多个小缓冲区，每包数据的接收到的长度需要额外保存

        当缓冲区满时，需要暂停接收，等待消费完毕，再开启接收
        DMA接收有点特殊，DMA传输完毕一种是自动暂停传输，另一种是需要手动停止传输，
        无论哪种都需要重新设置DMA参数再次开启

*/

/**
 * @brief 串口fifo 写入数据
 * @param obj 串口对象
 * @return uart_obj_sta_t 状态值
 * @note
 * 写入数据，会保存数据长度，并切换缓冲区，如果缓冲区已满，则暂停接收，等待消费完毕，再开启接收
 */
static uart_obj_sta_t uart_obj_rx_fifo_write(uart_obj_t* obj)
{
    // 内部调用，无需再次检验参数
    // #if(USE_UART_OBJ_FW_ASSERT == 1)
    // UART_OBJ_FW_ASSERT(obj == NULL);
    // #endif

    // 缓冲区已满
    if (obj->rx_fifo_data_volume == obj->init.rx_fifo_deep)
    {
        // 锁定缓冲区,不允许写入数据
        obj->rx_buf_lock = 1;
        // 暂停串口外设接收
        obj->init.uart_fifo_rx_stop_cb();

        return uart_fifo_wr_full;        // 缓冲区已满
    }

    // 保存这包数据，数据本身已经保存，这里保存长度
    obj->init.rx_fifo_manage[obj->rx_fifo_wr_cnt].rx_fifo_data_len = obj->rx_cnt_now;

    // 写计数+1 并判断回环
    obj->rx_fifo_wr_cnt = (obj->rx_fifo_wr_cnt + 1) % obj->init.rx_fifo_deep;

    // 切换缓冲区
    obj->rx_fifo_buf_cur_addr = obj->init.rx_fifo_buf_base_addr + (obj->rx_fifo_wr_cnt * obj->init.rx_fifo_buf_size_single);

    // 写入一包，数据量+1
    obj->rx_fifo_data_volume++;

    /*
            为什么要在这里判断满？
            当写入数量达到FIFO深度时就要及时暂停接收，而不是靠下一次写入时查询是否满，
            因为实际是先写缓冲区然后再触发FIFO写入，如果不及时暂停接收，会覆盖0号缓冲区，
            覆盖以后才发现缓冲区已满，这种方式和传统FIFO的实现方式不一样
    */
    // 判断缓冲区是否满
    if (obj->rx_fifo_data_volume == obj->init.rx_fifo_deep)
    {
        obj->rx_buf_lock = 1;        // 锁定缓冲区,不允许写入数据
        // 暂停串口外设接收
        obj->init.uart_fifo_rx_stop_cb();
        return uart_fifo_wr_full;        // 缓冲区已满
    }

#if (USE_DMA_MODE == 1)
    // dma 接收情况下切缓冲区需要重新配置一下接收参数，普通单字节接收不用
    obj->init.uart_fifo_rx_start_cb(obj->rx_fifo_buf_cur_addr, obj->init.rx_fifo_buf_size_single);
#endif

    return uart_fifo_wr_ok;
}

/**
 * @brief 串口fifo 读取数据
 * @param obj 串口对象
 * @return uart_obj_sta_t 状态值
 * @note 读取数据，会回调用户的api进行处理。
 * 	命名读数据只是参考电脑端和文件系统的命名方式，读写接口，
        对于串口来说，读并不准确，因为数据需要发送方发送了，你才读的到，
        这里的读实则为检测是否有数据可读，并回调用户的api进行处理
 */
uart_obj_sta_t uart_obj_rx_fifo_read(uart_obj_t* obj)
{
    uint8_t* rdata = NULL;
    uint16_t len   = 0;

#if (USE_UART_OBJ_FW_ASSERT == 1)
    UART_OBJ_FW_ASSERT(obj == NULL);
#endif

    if (obj->init_flag.value == 0)
    {
        return uart_obj_not_init;
    }

#if (USE_UART_TX_RX_MODE_SELECT == 1)
    // 当前模式处于只发送模式 ，不可以调用接收函数
    if (obj->init.uart_mode == FW_UART_TX_ONLY)
    {
        return uart_obj_mode_tx_only;
    }
#endif


    // 读取数据
    if (obj->rx_fifo_data_volume == 0)
    {
        return uart_fifo_rd_empty;        // 缓冲区为空
    }

    // 指针读取 基地址加上偏移量
    rdata = obj->init.rx_fifo_buf_base_addr + (obj->rx_fifo_rd_cnt * obj->init.rx_fifo_buf_size_single);
    len   = obj->init.rx_fifo_manage[obj->rx_fifo_rd_cnt].rx_fifo_data_len;
    // 回调给用户处理这一包数据
    obj->init.uart_fifo_rx_cb(rdata, len);

    // 读计数+1 并判断回环
    obj->rx_fifo_rd_cnt = (obj->rx_fifo_rd_cnt + 1) % obj->init.rx_fifo_deep;

    // 读一包数据量-1
    obj->rx_fifo_data_volume--;

    // 解锁缓冲区
    /*
            在数量为rx_fifo_deep时会锁定缓冲区数据，
            因为有空闲缓冲区让出来了
            在消费以后需要解锁
            当数据数量等于FIFO深度-1时，
    */
    if (obj->rx_fifo_data_volume == (obj->init.rx_fifo_deep - 1))
    {
        obj->rx_buf_lock = 0;        // 解锁缓冲区,允许写入数据
        // 开启串口外设接收
        obj->init.uart_fifo_rx_start_cb(obj->rx_fifo_buf_cur_addr, obj->init.rx_fifo_buf_size_single);
    }
    return uart_fifo_rd_ok;
}

/*------------------------------------------用户自定义库函数---------------------------------------------------------*/

// 轻量级printf库
#include "xprintf.h"

// 用户串口发送函数库
//#include "user_uart_obj.h"

void xprintf_put_char(int ch)
{
    // 根据所在平台修改
    //cw32_uart1_send_data((uint8_t*)&ch, 1);
}

void uart_obj_std_lib_redefine(void)
{
    xdev_out(xprintf_put_char);
}
